package com.zs.oauth2.oauth2authorizationserver.config;

import com.zs.oauth2.api.dto.UserInfo;
import com.zs.oauth2.common.core.constant.CacheConstants;
import com.zs.oauth2.common.core.constant.SecurityConstants;
import com.zs.oauth2.common.service.security.Oauth2User;
import com.zs.oauth2.oauth2authorizationserver.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Collection;

/**
 * @author madison
 * @description
 * @date 2021/4/4 14:14
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class Oauth2UserDetailsServiceImpl implements UserDetailsService {
//    private final RemoteUserService remoteUserService;

    private final UserService userService;

    // 必须配置RedisTemplateConfiguration 不然报错
    private final CacheManager cacheManager;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
        if (cache != null && cache.get(username) != null) {
            return (Oauth2User) cache.get(username).get();
        }

        UserInfo userInfo = userService.getUser(username);
//        userInfo.setPassword(new BCryptPasswordEncoder().encode("admin"));
        UserDetails userDetails = getUserDetails(userInfo);
        if (cache != null) {
            cache.put(username, userDetails);
        }
        return userDetails;
    }

    private UserDetails getUserDetails(UserInfo user) {
        Collection<? extends GrantedAuthority> authorities = AuthorityUtils
                .createAuthorityList();
        // 构造security用户
        // 作者在构造Security的User对象进行认证之前，进行了和处理clientSecret类似的操作，手动拼接了"{bcrypt}"的字符。
        // 作者的这两个操作，据我个人推测,应该是为了保证和Spring Security 4.x的密码格式的兼容性，隐藏密码变更的细节。
        return new Oauth2User(user.getUserId(), user.getUsername(),
                SecurityConstants.BCRYPT + user.getPassword(), true, true, true, true, authorities);
    }
}
